字典

dict类型是python语言的基石,所以python对于dict类型实现了高度优化,而 散列表 则是字典类型性能突出的根本原因。

什么是可散列的数据类型

定义:如果一个对象是可散列的,那么在这个对象的生命周期中,它的散列值是始终不变的,而且这个对象必定实现__hash__()方法。 另外,可散列对象还必须实现 __eq__()方法,这样才能保证两个相同类型的对象可以进行比较。如果两个对象是相同的,那么它们的散列值一定是相同的。

也就是说,一个可散列对象必须同时满足以下三个条件:

  1. 支持hash()方法,并且通过__hash__()方法所获得的散列值是不变的;
  2. 支持通过__eq__()方法来判断两个对象的值是否相等;
  3. 若 a==b,则必有 hash(a) == hash(b)。
  • 原子不可变数据类型(str、bytes和数值类型)都是可散列类型;
  • frozenset类型也是可散列的;
  • 当一个元组中所包含的值都是可散列的,该元组才是可散列的。

下例展示了创建字典的不同方式

>>> d1 = dict(one=1, two=2, three=3)
>>> d2 = {'one':1, 'two':2, 'three':3}
>>> d3 = dict(zip(['one', 'two', 'three'], [1,2,3]))
>>> d4 = dict([('two', 2), ('one', 1), ('three', 3)])
>>> d5 = dict({'one':1, 'two':2, 'three':3})
>>> d1 == d2 == d3 == d4 == d5
True


处理找不到的键

假设有一个字典dict,当程序试图查找一个不存在的键值 dict[k] 时,会抛出一个异常KeyError,这个行为复合python所信奉的“快速失败”哲学。当然,我们可以采用dict.get(k, default)来代替dict[k],当找不到键k时,返回默认值default,但是,这并不是一个高效的方式,也不是一个可取的方法。

1. setdefault方法

下面是一个案例:

dict_demo = {}
print(dict_demo)
key = "name"
dict_demo.get(key)
dict_demo.setdefault(key, [])  
print(dict_demo)
dict_demo.setdefault("pass", "123456")
print(dict_demo)

运行结果如下:

{}
{'name': []}
{'name': [], 'pass': '123456'}

代码中第一个 setdefault 中未找到 key,于是把一个空列表赋值到该键值

第二个 setdefault 未找到键"pass",将一个字符串赋值给该键

经过两个setdefault之后,该字典含有两个键值对,该方法主要用于对字典进行更新

2.defaultdict 处理空缺键的一个选择

在用户创建defaultdict对象时,需要给它配置一个为找不到的键创造默认值的方法

具体而言,就是在实例化一个defaultdict对象时,需要给构造方法赋予一个可调用对象,这个可调用对象在__getitem__碰到找不到的键时,让__getitem__返回一个默认值。

key = "name"
dict_demo2 = collections.defaultdict(list)
print(dict_demo2)
dict_demo2[key].append((1,2))
print(dict_demo2)
dict_demo2["pass"]
print(dict_demo2)

运行结果:

defaultdict(<class 'list'>, {})
defaultdict(<class 'list'>, {'name': [(1, 2)]})
defaultdict(<class 'list'>, {'name': [(1, 2)], 'pass': []})

dict_demo2一开始是一个空字典,不存在任何键值,当运行dict_demo2[key]时,其中包含一下三个步骤:

(1) 调用list()创建一个新列表

(2) 把这个新列表作为值,key作为键,放入dd中

(3) 返回这个列表的引用(这也是能够进行append操作的原因)

如果在键值不确定的情况下可以考虑使用defaultdict

3 特殊方法__missing__

所有映射类型在处理找不到的键时,都会牵扯__missing__方法,当__getitem__操作找不到键值时,就会调用__missing__方法,而不是直接抛出异常。

  • __missing__方法只会被__getitem__方法调用

    class StrKeyDict0(dict):

     def __missing__(self, key):
         if isinstance(key, str):
             raise KeyError(key)
         return self[str(key)]
    
     def get(self, key, default=None):
         try:
             return self[key]
         except KeyError:
             return default
    
     def __contains__(self, key):
         return key in self.keys() or str(key) in self.keys()
     
     

    if name == '__main__':

     d = StrKeyDict0([('2', 'two'), ('4', 'four')])
     print(d['2'])
     print(d[4])
     try:
         print(d[1])
     except KeyError:
         print("keyerror")
    
     print(d.get('2'))
     print(d.get(4))
     try:
         print(d.get(1, 'N/A'))
     except KeyError:
         print("keyerror")
    
     print(2 in d)
     print(4 in d)
    

运行结果:

two
four
keyerror
two
four
N/A
True
True

__missing__方法中将键key转化为str类型后再次尝试获取字典d中相应的键值

所以可以看到,即便字典中没有 4 这个键,但是依然能够正确获取其在字典中相应的值,但是由于字典中不存在 1 或 "1" 这样的键,所以无法获取d[1]


逸远尘红
1 声望2 粉丝